/* SPDX-License-Identifier: GPL-2.0-or-later */
 /*
 * lockglue.h
 *
 * Glue to the underlying cluster stack.
 *
 * Copyright (C) 2007 Oracle.  All rights reserved.
 */


#ifndef OCFS2_LOCK_GLUE_H
#define OCFS2_LOCK_GLUE_H

#include "dcache.h"
#include "ocfs2.h"
#include "alloc.h"
#include "refcounttree.h"

#define LOCK_CLASS_NAME_DLM "dlm"
#define LOCK_CLASS_NAME_ADL "adl"
#define LOCK_CLASS_NAME_LEN 3


#define OCFS2_LOCK_NL		0		/* null */
#define OCFS2_LOCK_PR		3	/* protected read */
#define OCFS2_LOCK_EX		5	/* exclusive */

struct ocfs2_lockglue_stack {
	char *stack_name;
	const struct ocfs2_lockglue_ops *l_ops;
};

/* ocfs2_inode_lock_full() 'arg_flags' flags */
/* don't wait on recovery. */
#define OCFS2_META_LOCK_RECOVERY	(0x01)
/* Instruct the dlm not to queue ourselves on the other node. */
#define OCFS2_META_LOCK_NOQUEUE		(0x02)
/* don't block waiting for the downconvert thread, instead return -EAGAIN */
#define OCFS2_LOCK_NONBLOCK		(0x04)

#define OCFS2_LOCK_DISK_ONLY	(0x08)
#define OCFS2_LOCK_DLM_ONLY		(0x10)

/* Locking subclasses of inode cluster lock */
enum {
	OI_LS_NORMAL = 0,
	OI_LS_PARENT,
	OI_LS_RENAME1,
	OI_LS_RENAME2,
	OI_LS_REFLINK_TARGET,
};


struct ocfs2_mask_waiter {
	struct list_head	mw_item;
	int			mw_status;
	struct completion	mw_complete;
	unsigned long		mw_mask;
	unsigned long		mw_goal;
#ifdef CONFIG_OCFS2_FS_STATS
	ktime_t			mw_lock_start;
#endif
};

struct ocfs2_lock_holder {
	struct list_head oh_list;
	struct pid *oh_owner_pid;
	int oh_ex;
	struct buffer_head *bh;
};

struct ocfs2_deadlock_seq_priv {
	struct ocfs2_deadlock_debug *p_deadlock_debug;
	struct ocfs2_lock_res p_iter_res;
	struct ocfs2_lock_res p_tmp_res;
	struct ocfs2_deadlock_lock_res_info p_res_info;
};

int ocfs2_lockstack_init(struct ocfs2_super *osb,
				enum ocfs2_lock_class class);

void ocfs2_lock_res_init_once(struct ocfs2_lock_res *res);
void ocfs2_inode_lock_res_init(struct ocfs2_lock_res *res,
				enum ocfs2_lock_type type,
				bool hung_check,
				bool hung_invalid,
				unsigned int generation,
				struct inode *inode);
void ocfs2_lockres_set_hung_info(struct ocfs2_lock_res *res,
				bool hung_check,
				bool hung_invalid);
void ocfs2_dentry_lock_res_init(struct ocfs2_dentry_lock *dl,
				u64 parent, struct inode *inode);
struct ocfs2_file_private;
void ocfs2_file_lock_res_init(struct ocfs2_lock_res *lockres,
				struct ocfs2_file_private *fp);
struct ocfs2_mem_dqinfo;
void ocfs2_qinfo_lock_res_init(struct ocfs2_lock_res *lockres,
				struct ocfs2_mem_dqinfo *info);
void ocfs2_refcount_lock_res_init(struct ocfs2_lock_res *lockres,
				struct ocfs2_super *osb, u64 ref_blkno,
				unsigned int generation);

void ocfs2_complete_all_lockres(struct o2nm_node *node, int node_num,
				void *data, u64 hb_generation);

void ocfs2_lock_res_free(struct ocfs2_super *osb, struct ocfs2_lock_res *res);
int ocfs2_create_new_inode_locks(struct inode *inode);
int ocfs2_drop_inode_locks(struct inode *inode);

int ocfs2_rw_lock(struct inode *inode, int ex);
void ocfs2_rw_unlock(struct inode *inode, int ex);
int ocfs2_open_lock(struct inode *inode);
int ocfs2_try_open_lock(struct inode *inode, int write);
void ocfs2_open_unlock(struct inode *inode);
int ocfs2_inode_lock_atime(struct inode *inode,
				struct vfsmount *vfsmnt,
				int *level);
int ocfs2_inode_lock_full_nested(struct inode *inode,
				struct buffer_head **ret_bh,
				int ex,
				int arg_flags,
				int subclass);
int ocfs2_inode_lock_tracker_full_nested(struct inode *inode,
				struct buffer_head **ret_bh,
				int ex,
				struct ocfs2_lock_holder **oh,
				int arg_flags,
				int subclass);
int ocfs2_inode_lock_with_page(struct inode *inode,
				struct buffer_head **ret_bh,
				int ex,
				struct page *page);
/* Variants without special locking class or flags */
#define ocfs2_inode_lock_full(i, r, e, f)\
		ocfs2_inode_lock_full_nested(i, r, e, f, OI_LS_NORMAL)
#define ocfs2_inode_lock_nested(i, b, e, s)\
		ocfs2_inode_lock_full_nested(i, b, e, 0, s)

#define ocfs2_inode_lock_tracker_full(i, r, e, t, f)\
		ocfs2_inode_lock_tracker_full_nested(i, r, e, t, f, OI_LS_NORMAL)
#define ocfs2_inode_lock_tracker_nested(i, b, e, t, s)\
		ocfs2_inode_lock_tracker_full_nested(i, b, e, t, 0, s)

/* 99% of the time we don't want to supply any additional flags --
 * those are for very specific cases only. */
#define ocfs2_inode_lock(i, b, e) ocfs2_inode_lock_full_nested(i, b, e, 0, OI_LS_NORMAL)

#define ocfs2_inode_unlock(i, e) ocfs2_inode_unlock_full(i, e, 0)

#define ocfs2_inode_lock_tracker(i, b, e, t)\
		ocfs2_inode_lock_tracker_full_nested(i, b, e, t, 0, OI_LS_NORMAL)

#define ocfs2_inode_unlock_tracker(i, e, t, h) ocfs2_inode_unlock_tracker_full(i, e, t, h, 0)

void ocfs2_inode_unlock_full(struct inode *inode, int ex, int flags);
void ocfs2_inode_unlock_tracker_full(struct inode *inode, int ex,
		struct ocfs2_lock_holder **oh, int had_lock, int flags);
void ocfs2_remove_holder(struct inode *inode,
		struct ocfs2_lock_holder **oh, int had_lock);
int ocfs2_super_block_lock(struct ocfs2_super *osb, int ex);
void ocfs2_super_block_unlock(struct ocfs2_super *osb, int ex);
int ocfs2_super_lock(struct ocfs2_super *osb, int ex);
void ocfs2_super_unlock(struct ocfs2_super *osb, int ex);
int ocfs2_orphan_scan_lock(struct ocfs2_super *osb, u32 *seqno);
void ocfs2_orphan_scan_unlock(struct ocfs2_super *osb, u32 seqno);
int ocfs2_rename_lock(struct ocfs2_super *osb);
void ocfs2_rename_unlock(struct ocfs2_super *osb);
int ocfs2_nfs_sync_lock(struct ocfs2_super *osb, int ex);
void ocfs2_nfs_sync_unlock(struct ocfs2_super *osb, int ex);
int ocfs2_dentry_lock(struct dentry *dentry, int ex);
void ocfs2_dentry_unlock(struct dentry *dentry, int ex);
int ocfs2_file_lock(struct file *file, int ex, int flags);
void ocfs2_file_unlock(struct file *file);
int ocfs2_is_flocked(struct ocfs2_super *osb, struct ocfs2_lock_res *res,
				int level);
int ocfs2_qinfo_lock(struct ocfs2_mem_dqinfo *oinfo, int ex);
void ocfs2_qinfo_unlock(struct ocfs2_mem_dqinfo *oinfo, int ex);
struct ocfs2_refcount_tree;
int ocfs2_refcount_lock(struct ocfs2_refcount_tree *ref_tree, int ex);
void ocfs2_refcount_unlock(struct ocfs2_refcount_tree *ref_tree, int ex);

void ocfs2_mark_lockres_freeing(struct ocfs2_super *osb,
				struct ocfs2_lock_res *lockres);
void ocfs2_simple_drop_lockres(struct ocfs2_super *osb,
				struct ocfs2_lock_res *lockres);
#ifdef ENABLE_DLM
void ocfs2_wake_downconvert_thread(struct ocfs2_super *osb);
#endif
struct ocfs2_lock_debug *ocfs2_new_lock_debug(void);
struct ocfs2_deadlock_debug *ocfs2_new_deadlock_debug(void);
void ocfs2_put_lock_debug(struct ocfs2_lock_debug *lock_debug);
void ocfs2_put_deadlock_debug(struct ocfs2_deadlock_debug *deadlock_debug);
void ocfs2_get_lock_debug(struct ocfs2_lock_debug *debug);
void ocfs2_get_deadlock_debug(struct ocfs2_deadlock_debug *deadlock_debug);
void ocfs2_add_lockres_tracking(struct ocfs2_lock_res *res,
		struct ocfs2_lock_debug *lock_debug);
void ocfs2_add_lockres_deadlock_tracking(struct ocfs2_lock_res *res,
		struct ocfs2_deadlock_debug *deadlock_debug);
void ocfs2_remove_lockres_tracking(struct ocfs2_lock_res *res);
void ocfs2_remove_lockres_deadlock_tracking(struct ocfs2_lock_res *res);
#ifdef ENABLE_DLM
void ocfs2_set_locking_protocol(void);
#endif
struct ocfs2_super *ocfs2_get_dentry_osb(struct ocfs2_lock_res *lockres);
struct ocfs2_super *ocfs2_get_inode_osb(struct ocfs2_lock_res *lockres);
struct ocfs2_super *ocfs2_get_file_osb(struct ocfs2_lock_res *lockres);
struct ocfs2_super *ocfs2_get_qinfo_osb(struct ocfs2_lock_res *lockres);

__u64 ocfs2_get_dentry_lock_ino(struct ocfs2_lock_res *lockres);

/*
 * OCFS2 Lock Resource Operations
 *
 * These fine tune the behavior of the generic dlmglue locking infrastructure.
 *
 * The most basic of lock types can point ->l_priv to their respective
 * struct ocfs2_super and allow the default actions to manage things.
 *
 * Right now, each lock type also needs to implement an init function,
 * and trivial lock/unlock wrappers. ocfs2_simple_drop_lockres()
 * should be called when the lock is no longer needed (i.e., object
 * destruction time).
 */
struct ocfs2_lock_res_ops {
	/*
	 * Translate an ocfs2_lock_res * into an ocfs2_super *. Define
	 * this callback if ->l_priv is not an ocfs2_super pointer
	 */
	struct ocfs2_super *(*get_osb)(struct ocfs2_lock_res *lockres);

	/*
	 * Optionally called in the downconvert thread after a
	 * successful downconvert. The lockres will not be referenced
	 * after this callback is called, so it is safe to free
	 * memory, etc.
	 *
	 * The exact semantics of when this is called are controlled
	 * by ->downconvert_worker()
	 */
	void (*post_unlock)(struct ocfs2_super *osb, struct ocfs2_lock_res *lockres);

	/*
	 * Allow a lock type to add checks to determine whether it is
	 * safe to downconvert a lock. Return 0 to re-queue the
	 * downconvert at a later time, nonzero to continue.
	 *
	 * For most locks, the default checks that there are no
	 * incompatible holders are sufficient.
	 *
	 * Called with the lockres spinlock held.
	 */
	int (*check_downconvert)(struct ocfs2_lock_res *lockres, int new_level);

	/*
	 * Allows a lock type to populate the lock value block. This
	 * is called on downconvert, and when we drop a lock.
	 *
	 * Locks that want to use this should set LOCK_TYPE_USES_LVB
	 * in the flags field.
	 *
	 * Called with the lockres spinlock held.
	 */
	void (*set_lvb)(struct ocfs2_lock_res *lockres);

	/*
	 * Called from the downconvert thread when it is determined
	 * that a lock will be downconverted. This is called without
	 * any locks held so the function can do work that might
	 * schedule (syncing out data, etc).
	 *
	 * This should return any one of the ocfs2_unblock_action
	 * values, depending on what it wants the thread to do.
	 */
	int (*downconvert_worker)(struct ocfs2_lock_res *lockres, int new_level);

	/*
	 * LOCK_TYPE_* flags which describe the specific requirements
	 * of a lock type. Descriptions of each individual flag follow.
	 */
	int flags;
};

/*
 * Some locks want to "refresh" potentially stale data when a
 * meaningful (PRMODE or EXMODE) lock level is first obtained. If this
 * flag is set, the OCFS2_LOCK_NEEDS_REFRESH flag will be set on the
 * individual lockres l_flags member from the ast function. It is
 * expected that the locking wrapper will clear the
 * OCFS2_LOCK_NEEDS_REFRESH flag when done.
 */
#define LOCK_TYPE_REQUIRES_REFRESH 0x1

/*
 * Indicate that a lock type makes use of the lock value block. The
 * ->set_lvb lock type callback must be defined.
 */
#define LOCK_TYPE_USES_LVB		0x2
static inline int ocfs2_is_inode_lock(struct ocfs2_lock_res *lockres)
{
	return lockres->l_type == OCFS2_LOCK_TYPE_META ||
		lockres->l_type == OCFS2_LOCK_TYPE_RW ||
		lockres->l_type == OCFS2_LOCK_TYPE_OPEN;
}

static inline struct inode *ocfs2_lock_res_inode(
				struct ocfs2_lock_res *lockres)
{
	BUG_ON(!ocfs2_is_inode_lock(lockres));

	return (struct inode *) lockres->l_priv;
}

static inline struct ocfs2_dentry_lock *ocfs2_lock_res_dl(
				struct ocfs2_lock_res *lockres)
{
	BUG_ON(lockres->l_type != OCFS2_LOCK_TYPE_DENTRY);

	return (struct ocfs2_dentry_lock *)lockres->l_priv;
}

static inline struct ocfs2_mem_dqinfo *ocfs2_lock_res_qinfo(
				struct ocfs2_lock_res *lockres)
{
	BUG_ON(lockres->l_type != OCFS2_LOCK_TYPE_QINFO);

	return (struct ocfs2_mem_dqinfo *)lockres->l_priv;
}

static inline struct ocfs2_refcount_tree *ocfs2_lock_res_refcount_tree(
				struct ocfs2_lock_res *res)
{
	return container_of(res, struct ocfs2_refcount_tree, rf_lockres);
}

static inline struct ocfs2_super *ocfs2_get_lockres_osb(
				struct ocfs2_lock_res *lockres)
{
	if (lockres->l_ops && lockres->l_ops->get_osb)
		return lockres->l_ops->get_osb(lockres);

	return (struct ocfs2_super *)lockres->l_priv;
}

static inline void ocfs2_inc_holders(struct ocfs2_lock_res *lockres,
				int level)
{
	BUG_ON(!lockres);

	switch (level) {
	case OCFS2_LOCK_EX:
		lockres->l_ex_holders++;
		lockres->l_ex_holder_pid = task_pid_nr(current);
		break;
	case OCFS2_LOCK_PR:
		lockres->l_ro_holders++;
		lockres->l_ro_holder_pid = task_pid_nr(current);
		break;
	default:
		BUG();
	}
}

static inline void ocfs2_dec_holders(struct ocfs2_lock_res *lockres,
				int level)
{
	BUG_ON(!lockres);

	switch (level) {
	case OCFS2_LOCK_EX:
		BUG_ON(!lockres->l_ex_holders);
		lockres->l_ex_holders--;
		lockres->l_ex_holder_pid = 0;
		break;
	case OCFS2_LOCK_PR:
		BUG_ON(!lockres->l_ro_holders);
		lockres->l_ro_holders--;
		lockres->l_ro_holder_pid = 0;
		break;
	default:
		BUG();
	}
}

void lockres_set_flags(struct ocfs2_lock_res *lockres,
				unsigned long newflags);
void lockres_or_flags(struct ocfs2_lock_res *lockres, unsigned long or);
void lockres_clear_flags(struct ocfs2_lock_res *lockres,
				unsigned long clear);

static inline int ocfs2_check_wait_flag(struct ocfs2_lock_res *lockres,
				int flag)
{
	unsigned long flags;
	int ret;

	spin_lock_irqsave(&lockres->l_lock, flags);
	ret = lockres->l_flags & flag;
	spin_unlock_irqrestore(&lockres->l_lock, flags);

	return ret;
}

static inline void ocfs2_wait_on_busy_lock(struct ocfs2_lock_res *lockres)
{
	wait_event(lockres->l_event,
		   !ocfs2_check_wait_flag(lockres, OCFS2_LOCK_BUSY));
}

static inline void ocfs2_wait_on_refreshing_lock(struct ocfs2_lock_res *lockres)
{
	wait_event(lockres->l_event,
		   !ocfs2_check_wait_flag(lockres, OCFS2_LOCK_REFRESHING));
}

static inline int ocfs2_check_wait_holders(struct ocfs2_lock_res *lockres)
{
	int ret;
	unsigned long flags;

	spin_lock_irqsave(&lockres->l_lock, flags);
	ret = (lockres->l_ro_holders || lockres->l_ex_holders);
	spin_unlock_irqrestore(&lockres->l_lock, flags);

	return ret;
}

static inline void ocfs2_wait_on_holders(struct ocfs2_lock_res *lockres)
{
	wait_event(lockres->l_event,
		   !ocfs2_check_wait_holders(lockres));
}

void ocfs2_init_mask_waiter(struct ocfs2_mask_waiter *mw);
int ocfs2_wait_for_mask(struct ocfs2_mask_waiter *mw);
void lockres_add_mask_waiter(struct ocfs2_lock_res *lockres,
				struct ocfs2_mask_waiter *mw,
				unsigned long mask,
				unsigned long goal);
int __lockres_remove_mask_waiter(struct ocfs2_lock_res *lockres,
				      struct ocfs2_mask_waiter *mw);
int lockres_remove_mask_waiter(struct ocfs2_lock_res *lockres,
				      struct ocfs2_mask_waiter *mw);
int ocfs2_wait_for_mask_interruptible(struct ocfs2_mask_waiter *mw,
					     struct ocfs2_lock_res *lockres);
void lockres_complete_mask_waiter(struct ocfs2_lock_res *lockres,
						int error);

int ocfs2_should_refresh_lock_res(struct ocfs2_lock_res *lockres);

/* If status is non zero, I'll mark it as not being in refresh
 * anymroe, but i won't clear the needs refresh flag. */
static inline void ocfs2_complete_lock_res_refresh(struct ocfs2_lock_res *lockres,
				int status)
{
	unsigned long flags;

	spin_lock_irqsave(&lockres->l_lock, flags);
	lockres_clear_flags(lockres, OCFS2_LOCK_REFRESHING);
	if (!status)
		lockres_clear_flags(lockres, OCFS2_LOCK_NEEDS_REFRESH);
	spin_unlock_irqrestore(&lockres->l_lock, flags);

	wake_up(&lockres->l_event);
}

int ocfs2_assign_bh(struct inode *inode,
				struct buffer_head **ret_bh,
				struct buffer_head *passed_bh);

int ocfs2_lock_init(struct ocfs2_super *osb);
void ocfs2_lock_exit(struct ocfs2_super *osb);
void ocfs2_mark_inode_res_freed(struct ocfs2_super *osb,
				struct inode *inode);
void ocfs2_clear_lowlevel_recovery_map(struct ocfs2_super *osb,
		unsigned int node_num);
void ocfs2_set_lowlevel_recovery_map(struct ocfs2_super *osb,
		unsigned int node_num);

void ocfs2_reschedule_hung_check_work(struct ocfs2_super *osb,
		struct inode *inode);

void ocfs2_print_lock_holder_pids(struct seq_file *m,
		struct ocfs2_lock_res *lockres);

void print_disk_lockres(struct ocfs2_super *osb,
		struct ocfs2_lock_res *lockres);

struct ocfs2_lockglue_ops {
	int (*inode_lock)(struct inode *inode, struct buffer_head **ret_bh,
			int ex, int arg_flags, int subclass);
	void (*inode_unlock)(struct inode *inode, int ex);
	int (*open_lock)(struct inode *inode);
	int (*try_open_lock)(struct inode *inode, int write);
	void (*open_unlock)(struct inode *inode);
	int (*create_inode_locks)(struct inode *inode);
	int (*rw_lock)(struct inode *inode, int write);
	void (*rw_unlock)(struct inode *inode, int write);
	int (*super_block_lock)(struct ocfs2_super *osb, int ex);
	void (*super_block_unlock)(struct ocfs2_super *osb, int ex);
	int (*super_lock)(struct ocfs2_super *osb, int ex);
	void (*super_unlock)(struct ocfs2_super *osb, int ex);
	int (*rename_lock)(struct ocfs2_super *osb);
	void (*rename_unlock)(struct ocfs2_super *osb);
	int (*nfs_sync_lock)(struct ocfs2_super *osb, int ex);
	void (*nfs_sync_unlock)(struct ocfs2_super *osb, int ex);
	int (*orphan_scan_lock)(struct ocfs2_super *osb, u32 *seqno);
	void (*orphan_scan_unlock)(struct ocfs2_super *osb, u32 seqno);
	int (*dentry_lock)(struct dentry *dentry, int ex);
	void (*dentry_unlock)(struct dentry *dentry, int ex);
	int (*file_lock)(struct file *file, int ex, int try_lock);
	void (*file_unlock)(struct file *file);
	int (*qinfo_lock)(struct ocfs2_mem_dqinfo *oinfo, int ex);
	void (*qinfo_unlock)(struct ocfs2_mem_dqinfo *oinfo, int ex);
	int (*refcount_lock)(struct ocfs2_refcount_tree *ref_tree, int ex);
	void (*refcount_unlock)(struct ocfs2_refcount_tree *ref_tree, int ex);
	int (*lock_init)(struct ocfs2_super *osb);
	void (*lock_exit)(struct ocfs2_super *osb);
	void (*lock_res_free)(struct ocfs2_lock_res *res);
	int (*drop_lock)(struct ocfs2_super *osb, struct ocfs2_lock_res *lockres);
	void (*super_block_lock_res_init)(struct ocfs2_lock_res *res,
			struct ocfs2_super *osb);
	void (*super_lock_res_init)(struct ocfs2_lock_res *res,
			struct ocfs2_super *osb);
	void (*rename_lock_res_init)(struct ocfs2_lock_res *res,
			struct ocfs2_super *osb);
	void (*nfs_sync_lock_res_init)(struct ocfs2_lock_res *res,
			struct ocfs2_super *osb);
	void (*orphan_scan_lock_res_init)(struct ocfs2_lock_res *res,
			struct ocfs2_super *osb);
	void (*dentry_lock_res_init)(struct ocfs2_lock_res *res,
			struct ocfs2_dentry_lock *dl, struct inode *inode);
	void (*inode_lock_res_init)(struct ocfs2_lock_res *res, enum ocfs2_lock_type type,
			unsigned int generation, struct inode *inode);
	void (*file_lock_res_init)(struct ocfs2_lock_res *res,
			struct ocfs2_file_private *fp);
	void (*qinfo_lock_res_init)(struct ocfs2_lock_res *res,
			struct ocfs2_mem_dqinfo *info);
	void (*refcount_lock_res_init)(struct ocfs2_lock_res *res,
			struct ocfs2_super *osb, u64 ref_blkno, unsigned int generation);
	void (*mark_lockres_freeing)(struct ocfs2_super *osb,
			struct ocfs2_lock_res *res);
	void (*lockres_complete_mask_waiter)(struct ocfs2_lock_res *lockres, int error);
	void (*mark_lockres_freed)(struct ocfs2_lock_res *lockres);
	void (*clear_recovery_map)(struct ocfs2_super *osb, unsigned int node_num);
	void (*set_recovery_map)(struct ocfs2_super *osb, unsigned int node_num);
};

static inline unsigned long ocfs2_lock_hung_check_ms(struct ocfs2_super *osb)
{
	unsigned long ret, flags;

	spin_lock_irqsave(&osb->osb_lock_hung_lock, flags);
	ret = osb->osb_lock_hung_ms;
	spin_unlock_irqrestore(&osb->osb_lock_hung_lock, flags);

	return ret;
}

#endif  /* OCFS2_LOCKGLUE_H */
